package data;

import constants.Difficulties;
import graphics.MULEPanel;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;

/**
 * data
 *
 * @author Jared Moore, Ivan Dario Jimenez
 * @date 10/9/13
 */
public class Store implements Serializable {
	private int mulesInStore;
	private final int[] prices = new int[4];
	private final int[] resources = new int[4];
	private final int[] resourcesBase = new int[4];
	private int money;
	private int moneyBase;
	public final static int costOfMule = 100;
	private JPanel self;

	private JLabel foodInLabel, oreInLabel, muleInLabel, energyInLabel, crystiteInLabel;

	public Store(Difficulties difficulty) {
		switch (difficulty) {
			case BEGINNER:
				mulesInStore = 25;
				money = 2000;
				moneyBase = money;
				//food
				prices[0] = 30;
				resources[0] = 16;
				resourcesBase[0] = 16;
				//ore
				prices[1] = 50;
				resources[1] = 0;
				resourcesBase[1] = 0;
				//energy
				prices[2] = 25;
				resources[2] = 16;
				resourcesBase[2] = 16;
				//crystite
				prices[3] = 100;
				resources[3] = 0;
				resourcesBase[3] = 0;
				break;
			case STANDARD:
                mulesInStore = 15;
                money = 1500;
                moneyBase = money;
                //food
                prices[0] = 50;
                resources[0] = 45;
                resourcesBase[0] = 45;
                //ore
                prices[1] = 110;
                resources[1] = 15;
                resourcesBase[1] = 15;
                //energy
                prices[2] = 80;
                resources[2] = 25;
                resourcesBase[2] = 25;
				break;
			case TOURNAMENT:
                mulesInStore = 15;
                money = 1500;
                moneyBase = money;
                //food
                prices[0] = 55;
                resources[0] = 50;
                resourcesBase[0] = 50;
                //ore
                prices[1] = 120;
                resources[1] = 15;
                resourcesBase[1] = 15;
                //energy
                prices[2] = 85;
                resources[2] = 20;
                resourcesBase[2] = 20;				break;
			default:
				mulesInStore = 14;
				money = 2000;
				moneyBase = money;
				//food
				prices[0] = 30;
				resources[0] = 8;
				resourcesBase[0] = 8;
				//ore
				prices[1] = 50;
				resources[1] = 8;
				resourcesBase[1] = 8;
				//energy
				prices[2] = 25;
				resources[2] = 8;
				resourcesBase[2] = 8;
				//crystite
				prices[3] = 100;
				resources[3] = 0;
				resourcesBase[3] = 0;
		}
	}

	/**
	 * recalculates prices to match demand and supply
	 * to use after turn has ended
	 */
	public void recalculatePrices() {
	/*	double priceMultiplier = money / ((double) moneyBase);
		for (int x = 0; x < prices.length; x++) {
			if (x == 3) {
				continue;
			} else {
				prices[x] = (int) Math.round(prices[x] * priceMultiplier * (resources[x] / (double) resourcesBase[x]));
			}
		}*/
	}

	/**
	 * Attempts to sell the corresponding units of food to the amount of energy given
	 * adds money from the sale
	 * subtracts ore from inventory
	 *
	 * @param money the amount of money for the total cost of the sale of food
	 * @return the amount of food sold
	 */
	public int sellFood(int money) {
		int units = money / prices[0];
		if (!hasFood(units)) {
			JOptionPane.showMessageDialog(null, "The shop does not have enough food for that",
					"Error", JOptionPane.ERROR_MESSAGE);
			return 0;
		}
		this.money += money;
		resources[0] -= units;
		return units;
	}

	/**
	 * attempts to sell ore from the store corresponding to the given amount of money
	 * adds money from the sale
	 * subtracts ore from inventory
	 *
	 * @param money the amount of money for the total cost of the sale of ore
	 * @return the amount of ore sold
	 */
	public int sellOre(int money) {
		int units = money / prices[1];
		if (!hasOre(units)) {
			JOptionPane.showMessageDialog(null, "The shop does not have enough ore for that",
					"Error", JOptionPane.ERROR_MESSAGE);
			return 0;
		}
		this.money += money;
		resources[1] -= units;
		return units;
	}

	/**
	 * attempts to sell energy from the store corresponding to the given amount of money
	 * adds the money from the sale
	 * subtracts the energy from inventory
	 *
	 * @param money the amount of money for the total cost of the sale of energy
	 * @return the amount of energy sold
	 */
	public int sellEnergy(int money) {
		int units = money / prices[2];
		if (!hasEnergy(units)) {
			JOptionPane.showMessageDialog(null, "The shop does not have enough energy for that",
					"Error", JOptionPane.ERROR_MESSAGE);
			return 0;
		}
		this.money += money;
		resources[2] -= units;
		return units;
	}

	/**
	 * attempts to sell crystite from the store corresponding to the given amount of money
	 * adds the money from the sale
	 * subtracts the crystite from inventory
	 *
	 * @param money the amount of money for the total cost of the sale of crystite
	 * @return the amount of crystite sold
	 */
	public int sellCrystite(int money) {
		int units = money / prices[3];
		if (!hasCrystite(units)) {
			JOptionPane.showMessageDialog(null, "The shop does not have enough crystite for that",
					"Error", JOptionPane.ERROR_MESSAGE);
			return 0;
		}
		this.money += money;
		resources[3] -= units;
		return units;
	}

	public void sellMule() {
		mulesInStore--;
	}

	/**
	 * attempts to make store buy the specified amount of food
	 * subtracts the money corresponding to the total cost of the food
	 * adds food to the inventory
	 *
	 * @param food the amount of food to be bought
	 * @return the cost of the food
	 */
	public int buyFood(int food) {
		int total = food * prices[0];
		if (!hasMoney(total)) {
			JOptionPane.showMessageDialog(null, "The shop does not have enough money for that",
					"Error", JOptionPane.ERROR_MESSAGE);
			return 0;
		}
		resources[0] += food;
		money -= total;
		return total;
	}

	/**
	 * attempts to make store buy the specified amount of ore
	 * subtracts the money corresponding to the total cost of the ore
	 * adds ore to the inventory
	 *
	 * @param ore the amount of ore to be bought
	 * @return the cost of the ore
	 */
	public int buyOre(int ore) {
		int total = ore * prices[1];
		if (!hasMoney(total)) {
			JOptionPane.showMessageDialog(null, "The shop does not have enough money for that",
					"Error", JOptionPane.ERROR_MESSAGE);
			return 0;
		}
		resources[1] += ore;
		money -= total;
		return total;
	}

	/**
	 * attempts to make store buy the specified amount of energy
	 * subtracts the money corresponding to the total cost of the energy
	 * adds energy to the inventory
	 *
	 * @param energy the amount of energy to be bought
	 * @return the cost of the energy
	 */
	public int buyEnergy(int energy) {
		int total = energy * prices[2];
		if (!hasMoney(total)) {
			JOptionPane.showMessageDialog(null, "The shop does not have enough money for that",
					"Error", JOptionPane.ERROR_MESSAGE);
			return 0;
		}
		resources[2] += energy;
		money -= total;
		return total;
	}

	/**
	 * attempts to make store buy the specified amount of crystite
	 * subtracts the money corresponding to the total cost of the crystite
	 * adds crystite to the inventory
	 *
	 * @param crystite the amount of crystite to be bought
	 * @return the cost of the crystite
	 */
	public int buyCrystite(int crystite) {
		int total = crystite * prices[3];
		if (!hasMoney(total)) {
			JOptionPane.showMessageDialog(null, "The shop does not have enough money for that",
					"Error", JOptionPane.ERROR_MESSAGE);
			return 0;
		}
		resources[3] += crystite;
		money -= total;
		return total;
	}

	/**
	 * @return the price of a unit of food
	 */
	public int getFoodPrice() {
		return prices[0];
	}

	/**
	 * @return the price of a unit of ore
	 */
	public int getOrePrice() {
		return prices[1];
	}

	/**
	 * @return the price of a unit of energy
	 */
	public int getEnergyPrice() {
		return prices[2];
	}

	/**
	 * @return the price of a unit of energy
	 */
	public int getCrystitePrice() {
		return prices[3];
	}

	/**
	 * @return the store's stock of mules
	 */
	public int getMuleStock() {
		return mulesInStore;
	}

	/**
	 * @return the store's stock of food
	 */
	public int getFoodStock() {
		return resources[0];
	}

	/**
	 * @return the store's stock of ore
	 */
	public int getOreStock() {
		return resources[1];
	}

	/**
	 * @return the store's stock of energy
	 */
	public int getEnergyStock() {
		return resources[2];
	}
	
	/**
	 * 
	 * @return the amount of money the store has
	 */
	public int getMoney() {
		return (money);
	}

	/**
	 * @return the store's stock of crystite
	 */
	public int getCrystiteStock() {
		return resources[3];
	}

	/**
	 * Checks to see if the store has at least that amount of money
	 *
	 * @param amount the amount of money to be tested
	 * @return true if the store has at least that amount of money else false
	 */
	public boolean hasMoney(int amount) {
		return amount <= money;
	}

	/**
	 * Checks to see if the store has at least that amount of food
	 *
	 * @param amount the amount of food to be tested
	 * @return true if the store has at least that amount of food else false
	 */
	public boolean hasFood(int amount) {
		return amount <= getFoodStock();
	}

	/**
	 * Checks to see if the store has at least that amount of ore
	 *
	 * @param amount the amount of ore to be tested
	 * @return true if the store has at least that amount of ore else false
	 */
	public boolean hasOre(int amount) {
		return amount <= getOreStock();
	}

	/**
	 * Checks to see if the store has at least that amount of energy
	 *
	 * @param amount the amount of energy to be tested
	 * @return true if the store has at least that amount of energy else false
	 */
	public boolean hasEnergy(int amount) {
		return amount <= getEnergyStock();
	}

	/**
	 * Checks to see if the store has at least that amount of crystite
	 *
	 * @param amount the amount of energy to be tested
	 * @return true if the store has at least that amount of energy else false
	 */
	public boolean hasCrystite(int amount) {
		return amount <= getCrystiteStock();
	}

	/**
	 * checks to see if the store has at least the maximum amount of mules a player can have
	 *
	 * @return true if the store has the amount of mules else false
	 */
	public boolean hasMULE() {
		return mulesInStore >= MULE.MAX_MULE_NUM;
	}

	/**
	 * calls all logic regarding buying and selling in the store
	 *
	 * @param upperPanel panel where the store is going to be placed
	 */
	public void drawStore(final MULEPanel upperPanel) {
		final Store currentStore = this;

		final JPanel store = new JPanel();
		self = store;
		store.setLayout(new BorderLayout());

		JLabel welcomeInfo = new JLabel("Welcome to the Shop");
		foodInLabel = new JLabel(String.format("<html>Food<br/>At %d per unit<br/>%d Remaining",
				getFoodPrice(), getFoodStock()));
		oreInLabel = new JLabel(String.format("<html>Ore<br/>At %d per unit<br/>%d Remaining",
				getOrePrice(), getOreStock()));

		energyInLabel = new JLabel(String.format("<html>Energy<br/>At %d per unit<br/>%d Remaining",
				getEnergyPrice(), getEnergyStock()));

		crystiteInLabel = new JLabel(String.format("<html>Crystite<br/>At %d per unit<br/>%d Remaining",
				getCrystitePrice(), getCrystiteStock()));

		muleInLabel = new JLabel(String.format("<html>M.U.L.E (can only buy one at a time)" +
				"<br/>At %d per unit<br/>%d Remaining",
				200, getMuleStock()));

		final JTextField foodIn = new JTextField("0");
		final JTextField oreIn = new JTextField("0");
		final JTextField energyIn = new JTextField("0");
		final JTextField crystiteIn = new JTextField("0");

		String[] selectionValues = {"yes", "no"};
		final JComboBox<String> muleIn = new JComboBox<>(selectionValues);
		muleIn.setSelectedIndex(1);

		JPanel numbers = new JPanel();
		numbers.setLayout(new GridLayout(5, 2));
		numbers.add(foodInLabel);
		numbers.add(foodIn);
		numbers.add(oreInLabel);
		numbers.add(oreIn);
		numbers.add(energyInLabel);
		numbers.add(energyIn);
		numbers.add(crystiteInLabel);
		numbers.add(crystiteIn);
		numbers.add(muleInLabel);
		numbers.add(muleIn);

		JButton buy = new JButton("Buy");
		buy.setPreferredSize(new Dimension(100, 100));
		buy.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent actionEvent) {
				try {
					int food = Integer.parseInt(foodIn.getText());
					int ore = Integer.parseInt(oreIn.getText());
					int energy = Integer.parseInt(energyIn.getText());
					int crystite = Integer.parseInt(crystiteIn.getText());
					boolean mule = muleIn.getSelectedItem().equals("yes");
					if (food < 0 || ore < 0 || energy < 0 || crystite < 0) {
						throw new Exception();
					}

					if (!Shopping.PlayerCanMakePurchase(upperPanel.getCurrPlayer(), currentStore, food, ore,
							energy, crystite, mule)) {
						String errorMessage = "Please Make Sure You have enough money, the space for a MULE, and " +
								"that the store has enough stock to make this purchase";
						String title = "Invalid Purchase Input";
						JOptionPane.showMessageDialog(null, errorMessage, title, JOptionPane.ERROR_MESSAGE);
					} else {
						Shopping.PlayerMakesPurchase(upperPanel.getCurrPlayer(), currentStore, food, ore,
								energy, crystite, mule);
						updateQuantities();
					}

					muleIn.setSelectedIndex(1);

					foodIn.setText("0");
					oreIn.setText("0");
					energyIn.setText("0");
					crystiteIn.setText("0");

					upperPanel.repaint();

				} catch (Exception e) {
					String errorMessage = "Please enter a non-negative integer";
					String title = "Invalid Number Input";
					JOptionPane.showMessageDialog(null, errorMessage, title, JOptionPane.ERROR_MESSAGE);
				}
			}
		});

		JButton sell = new JButton("Sell");
		sell.addActionListener(new AbstractAction() {
			@Override
			public void actionPerformed(ActionEvent actionEvent) {
				try {
					int food = Integer.parseInt(foodIn.getText());
					int ore = Integer.parseInt(oreIn.getText());
					int energy = Integer.parseInt(energyIn.getText());
					int crystite = Integer.parseInt(crystiteIn.getText());

					if (food < 0 || ore < 0 || energy < 0 || crystite < 0) {
						throw new Exception();
					}

					if (!Shopping.PlayerCanMakeSale(upperPanel.getCurrPlayer(), currentStore, food, ore,
							energy, crystite)) {
						String errorMessage = "Please make sure the store has enough energy and" +
								" that you have enough stock to sell";

						String title = "Invalid Purchase Input";
						JOptionPane.showMessageDialog(null, errorMessage, title, JOptionPane.ERROR_MESSAGE);
					} else {
						Shopping.PlayerMakesSale(upperPanel.getCurrPlayer(), currentStore, food, ore,
								energy, crystite);
						updateQuantities();
					}

					muleIn.setSelectedIndex(1);

					foodIn.setText("0");
					oreIn.setText("0");
					energyIn.setText("0");
					crystiteIn.setText("0");

					upperPanel.repaint();

				} catch (Exception e) {
					String errorMessage = "Please enter a non-negative integer";
					String title = "Invalid Number Input";
					JOptionPane.showMessageDialog(null, errorMessage, title, JOptionPane.ERROR_MESSAGE);
				}
			}
		});

		JButton exit = new JButton("Exit");
		exit.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent actionEvent) {
				upperPanel.remove(store);
				upperPanel.repaint();
			}
		});

		JPanel buttons = new JPanel();
		buttons.setLayout(new GridLayout(1, 3));
		buttons.add(buy);
		buttons.add(sell);
		buttons.add(exit);

		JPanel title = new JPanel();
		title.setLayout(new BorderLayout());
		title.add(welcomeInfo, BorderLayout.CENTER);

		store.add(buttons, BorderLayout.SOUTH);
		store.add(numbers, BorderLayout.CENTER);
		store.add(title, BorderLayout.NORTH);

		upperPanel.add(store, BorderLayout.CENTER);

		upperPanel.repaint();
	}

	public void removeStore(JPanel upperPanel) {
		upperPanel.remove(self);
		upperPanel.repaint();
	}

	public void updateQuantities() {
		foodInLabel.setText(String.format("<html>Food<br/>At %d per unit<br/>%d Remaining",
				getFoodPrice(), getFoodStock()));

		oreInLabel.setText(String.format("<html>Ore<br/>At %d per unit<br/>%d Remaining",
				getOrePrice(), getOreStock()));

		energyInLabel.setText(String.format("<html>Energy<br/>At %d per unit<br/>%d Remaining",
				getEnergyPrice(), getEnergyStock()));

		muleInLabel.setText(String.format("<html>M.U.L.E (can only buy one at a time)" +
				"<br/>At %d per unit<br/>%d Remaining",
				200, getMuleStock()));

		crystiteInLabel.setText(String.format("<html>Crystite<br/>At %d per unit<br/>%d Remaining",
				getCrystitePrice(), getCrystiteStock()));

		self.repaint();
	}
}

